/*
 * Copyright © 2017 eqxiu.com 北京中网易企秀科技有限公司  All rights reserved.
 */
package cn.hermit.data.xml.model;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;

import javax.xml.namespace.QName;

import cn.hermit.util.ReflectionUtils;
import cn.hermit.util.StringUtils;

/**
 * Java model of XML Element.
 * 
 * @author Jack Gao (Chinese name : GAO JIANGUO, Email : linux.gjg@gmail.com)
 * @date 19 Jun, 2014
 */
public class XmlElement extends XmlNameNode {

	/**
	 * 
	 */
	private static final long serialVersionUID = -6217162770312524147L;

	Collection<XmlNode> children;

	Collection<XmlAttribute> attributes;

	boolean closedTag;

	XmlElement() {
	}

	/**
	 * Get size of children.
	 * 
	 * @return
	 * 		Size of children
	 */
	public int childrenCount() {
		return children.size();
	}

	/**
	 * Get iterator of children.
	 * 
	 * @return
	 * 		Iterator of children
	 */
	public Iterator<XmlNode> childrenIterator() {
		return children.iterator();
	}

	/**
	 * Get size of attributes.
	 * 
	 * @return
	 * 		Size of attributes
	 */
	public int attributeSize() {
		return attributes.size();
	}

	/**
	 * Get iterator of attributes.
	 * 
	 * @return
	 * 		Iterator of attributes
	 */
	public Iterator<XmlAttribute> attributeIterator() {
		return attributes.iterator();
	}

	/**
	 * Get attributes which are NameSpace.
	 * 
	 * @return
	 * 		Attributes which are NameSpace
	 */
	public Collection<XmlAttribute> getNamespaceAttributes() {
		if (ReflectionUtils.isEmpty(attributes))
			return Collections.emptyList();
		Collection<XmlAttribute> attributes = new ArrayList<XmlAttribute>();
		for (XmlAttribute attribute : this.attributes) {
			if (isNamespaceName(attribute.name)) {
				attributes.add(attribute);
			}
		}
		return attributes;
	}

	/**
	 * Get attribute by QName.
	 * 
	 * @param name
	 * 		The QName
	 * @return
	 * 		An attribute
	 */
	public XmlAttribute getAttribute(QName name) {
		for (XmlAttribute attr : this.attributes) {
			if (attr.getName().equals(name)) {
				return attr;
			}
		}
		return null;
	}

	/**
	 * Get elements from children.
	 * 
	 * @return
	 * 		Elements from children
	 */
	public Collection<XmlElement> getElements() {
		if (ReflectionUtils.isEmpty(children))
			return Collections.emptyList();
		Collection<XmlElement> elems = new ArrayList<XmlElement>();
		for (XmlNode child : children) {
			if (XmlElement.class.isInstance(child)) {
				elems.add((XmlElement) child);
			}
		}
		return elems;
	}
	
	/**
	 * Get texts from children.
	 * 
	 * @return
	 * 		texts from children
	 */
	public Collection<XmlText> getTexts() {
		if (ReflectionUtils.isEmpty(children))
			return Collections.emptyList();
		Collection<XmlText> texts = new ArrayList<XmlText>();
		for (XmlNode child : children) {
			if (XmlText.class == child.getClass()
					|| XmlCData.class == child.getClass()) {
				texts.add((XmlText) child);
			}
		}
		return texts;
	}

	/**
	 * Get comments from children.
	 * 
	 * @return
	 * 		comments from children
	 */
	public Collection<XmlText> getComments() {
		if (ReflectionUtils.isEmpty(children))
			return Collections.emptyList();
		Collection<XmlText> texts = new ArrayList<XmlText>();
		for (XmlNode child : children) {
			if (XmlComment.class == child.getClass()) {
				texts.add((XmlText) child);
			}
		}
		return texts;
	}

	/**
	 * Check if the element is a tag-closed element.
	 * 
	 * @return
	 */
	public boolean isClosedTag() {
		return closedTag;
	}

	/**
     * Serialize XML element to XML string.
     */
	public String toString() {
		StringBuilder b = new StringBuilder();
		String fullName = getName(name);
		b.append('<').append(fullName);
		if (!ReflectionUtils.isEmpty(attributes)) {
			for (XmlAttribute attribute : this.attributes) {
				b.append(' ').append(attribute.toString());
			}
		}
		if (this.closedTag) {
			b.append("/>");
			return b.toString();
		}
		b.append('>');
		if (!ReflectionUtils.isEmpty(children)) {
			for (XmlNode child : this.children) {
				b.append(child.toString());
			}
		}
		b.append("</").append(fullName).append('>');
		return b.toString();
	}

	@Override
	public String toFormatString(int depth, String newLineFeed,
			boolean newLineForTexts) {
		StringBuilder b = new StringBuilder();
		String fullName = getName(name);
		b.append(StringUtils.tabs(depth)).append('<').append(fullName);
		if (!ReflectionUtils.isEmpty(attributes)) {
			for (XmlAttribute attribute : this.attributes) {
				b.append(' ').append(attribute.toString());
			}
		}
		if (this.closedTag) {
			b.append("/>");
			return b.toString();
		}
		b.append('>');
		if (ReflectionUtils.isEmpty(children)) {
			b.append("</").append(fullName).append('>');
		} else {
			if (newLineForTexts) {
				b.append(newLineFeed);
				for (XmlNode child : this.children) {
					b.append(
							child.toFormatString(depth + 1, newLineFeed,
									newLineForTexts)).append(newLineFeed);
				}
				b.append(StringUtils.tabs(depth)).append("</").append(fullName)
						.append('>');
			} else {
				XmlNode lastChild = null;
				for (XmlNode child : this.children) {
					if (child.isPureText()) {
						b.append(child.toString());
					} else {
						if (lastChild == null) {
							b.append(newLineFeed);
						}
						b.append(
								child.toFormatString(depth + 1, newLineFeed,
										newLineForTexts)).append(newLineFeed);
					}
					lastChild = child;
				}
				if (lastChild.isPureText()) {
					b.append("</").append(fullName).append('>');
				} else {
					b.append(StringUtils.tabs(depth)).append("</").append(fullName)
							.append('>');
				}
			}
		}
		return b.toString();
	}

	@Override
	public boolean isElement() {
		return true;
	}

	/**
	 * Clear attributes from element.
	 */
	public void clearAttributes() {
		this.attributes.clear();
	}

	/**
	 * Clear children from element.
	 */
	public void clearChildren() {
		this.children.clear();
	}

}